﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;

namespace LunchCommand.Models
{
    public class Venue : IEquatable<Venue>
    {
        [NotMapped]
        private Lazy<DateTime?> LastVisitLazy;

        [NotMapped]
        private Lazy<DateTime?> LastVisitAsQuickLazy;

        public Venue()
        {
            this.MinWeeksBetweenVisits = 1;
            this.DateCreated = DateTime.Now;
            this.DaysOfWeek = new VenueDaysOfWeek();

            this.LastVisitLazy = new Lazy<DateTime?>(() =>
            {
                if (this.Lunches == null || !this.Lunches.Any())
                {
                    return null;
                }

                return this.Lunches.Max(l => l.LunchTime);
            });

            this.LastVisitAsQuickLazy = new Lazy<DateTime?>(() =>
            {
                if (this.QuickLunches == null || !this.QuickLunches.Any())
                {
                    return null;
                }

                return this.QuickLunches.Max(l => l.LunchTime);
            });
        }

        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Int32 VenueID { get; set; }

        [Index(IsUnique = true)]
        [MaxLength(50)]
        [MinLength(3)]
        [Required]
        public String Name { get; set; }

        [MaxLength(1000)]
        [MinLength(10)]
        public String Description { get; set; }

        [MaxLength(1000)]
        [MinLength(10)]
        public String Address { get; set; }

        public Int32 GenreID { get; set; }

        public Boolean IsQuick { get; set; }

        [Required]
        public Int32 MinWeeksBetweenVisits { get; set; }

        public VenueDaysOfWeek DaysOfWeek { get; set; }

        [Index]
        public Boolean Deleted { get; set; }

        public DateTime DateCreated { get; set; }

        [ForeignKeyAttribute("CreatedById")]
        public virtual User CreatedBy { get; set; }

        public Int32? CreatedById { get; set; }

        /// <summary>
        /// Special venues are those that are good for both quick and normal lunches.
        /// </summary>
        public Boolean IsSpecial { get; set; }

        public virtual Genre Genre { get; set; }

        public virtual ICollection<UserVenueChoice> UsersWhoChose { get; set; }

        [InverseProperty("Venue")]
        public virtual ICollection<Lunch> Lunches { get; set; }

        [InverseProperty("QuickVenue")]
        public virtual ICollection<Lunch> QuickLunches { get; set; }

        public DateTime? LastVisited
        {
            get
            {
                return this.LastVisitLazy.Value;
            }
        }

        public DateTime? LastVisitedAsQuick
        {
            get
            {
                return this.LastVisitAsQuickLazy.Value;
            }
        }

        public Int32? DaysSinceLastVisit
        {
            get
            {
                if (!this.LastVisited.HasValue)
                {
                    return null;
                }

                return (int)Math.Round((DateTime.Now - this.LastVisited.Value).TotalDays);
            }
        }

        public Int32? DaysSinceLastVisitAsQuick
        {
            get
            {
                if (!this.LastVisitedAsQuick.HasValue)
                {
                    return null;
                }

                return (int)Math.Round((DateTime.Now - this.LastVisitedAsQuick.Value).TotalDays);
            }
        }

        public String LastVisitedString
        {
            get
            {
                if (this.DaysSinceLastVisit == null)
                {
                    return "Never";
                }
                else
                {
                    switch (this.DaysSinceLastVisit)
                    {
                        case 0:
                            return "Today";
                        case 1:
                            return "Yesterday";
                        default:
                            return String.Format("{0} days ago", this.DaysSinceLastVisit);
                    }
                }
            }
        }


        public bool Equals(Venue other)
        {
            if (other == null)
            {
                return false;
            }

            return this.GetHashCode() == other.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            return this.Equals(obj as Venue);
        }

        public override int GetHashCode()
        {
            if (this.VenueID <= 0)
            {
                return this.Name.GetHashCode();
            }

            return this.VenueID;
        }

        public Int32 GetDaysSinceLastVisit(Boolean asQuick, DateTime def)
        {
            DateTime date = DateTime.Now;
            switch (asQuick)
            {
                case true:
                    date = this.LastVisitedAsQuick.GetValueOrDefault(def);
                    break;
                case false:
                    date = this.LastVisited.GetValueOrDefault(def);
                    break;
            }

            return (int)(DateTime.Today - date).TotalDays;
        }
    }
}